package com.appspot.mobileirisnextbus.server;

import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.StringReader;
import java.io.StringWriter;
import java.io.UnsupportedEncodingException;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLEncoder;
import java.nio.charset.Charset;

import javax.xml.transform.Source;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerConfigurationException;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.TransformerFactoryConfigurationError;
import javax.xml.transform.stream.StreamResult;
import javax.xml.transform.stream.StreamSource;

import org.apache.commons.lang3.StringUtils;

import com.appspot.mobileirisnextbus.shared.Constants;

public class SimpleBusServiceImpl {

    private static final String USER_AGENT = "User-Agent";
    private static final String UTF_8 = "UTF-8";
    private static final String URL_DEST = "http://m.mytransport.sg/mtc/bus_services/arrival.html?busStopCode=";
    private static final String UA_IPHONE = "Mozilla/5.0 (iPhone; U; CPU iPhone OS 4_3_3 like Mac OS X; en-us) AppleWebKit/533.17.9 (KHTML, like Gecko) Version/5.0.2 Mobile/8J2 Safari/6533.18.5";
    private static String uaUrl = "";
    static {
        try {
            SimpleBusServiceImpl.uaUrl = "ua="
                    + URLEncoder.encode(SimpleBusServiceImpl.UA_IPHONE, SimpleBusServiceImpl.UTF_8);
        } catch (UnsupportedEncodingException e) {
            // do nothing
            e.printStackTrace(System.err);
        }
    }

    public String generateFinalMarkup(String rawInput, String xslPath)
            throws TransformerFactoryConfigurationError, TransformerConfigurationException,
            TransformerException {
        rawInput = this.trimRawData(rawInput);
        Source inputSource = new StreamSource(new StringReader(rawInput));
        Source xslSource = new StreamSource(new File(xslPath));

        TransformerFactory factory = TransformerFactory.newInstance();
        Transformer transformer = factory.newTransformer(xslSource);

        StringWriter sw = new StringWriter();
        transformer.transform(inputSource, new StreamResult(sw));
        String finalResult = sw.toString();
        return finalResult;
    }

    private String trimRawData(String input) {
        String rawInput = input;
        rawInput = rawInput.substring(rawInput.indexOf("<html"));
        rawInput = StringUtils.normalizeSpace(rawInput);
        rawInput = rawInput.substring(0, rawInput.indexOf("<head>"))
                + rawInput.substring(rawInput.indexOf("<body "));
        rawInput = rawInput.substring(0, rawInput.indexOf("<select "))
                + rawInput.substring((rawInput.indexOf("</select>") + "</select>".length()));
        rawInput = StringUtils.remove(rawInput, "xmlns=\"http://www.w3.org/1999/xhtml\"");
        rawInput = StringUtils.remove(rawInput, "&");
        return rawInput;
    }

    public String getRawData(String busNum) throws MalformedURLException, IOException {
        String finalUrl = SimpleBusServiceImpl.URL_DEST + busNum;
        URL url = new URL(finalUrl);
        HttpURLConnection conn = (HttpURLConnection) url.openConnection();

        conn.setRequestProperty(SimpleBusServiceImpl.USER_AGENT, SimpleBusServiceImpl.UA_IPHONE);
        conn.setInstanceFollowRedirects(false);
        conn.setDoInput(true);

        int responseCode = this.getResponseCode(conn);
        boolean redirect = this.isRedirect(responseCode);
        while (redirect) {
            conn = this.handleRedirect(SimpleBusServiceImpl.uaUrl, finalUrl, conn);
            redirect = this.isRedirect(this.getResponseCode(conn));
        }

        // print final response
        String responseAsString = this.getResponseAsString(conn);
        return responseAsString;
    }

    private boolean isRedirect(int responseCode) {
        boolean redirect = responseCode == HttpURLConnection.HTTP_MOVED_TEMP;
        return redirect;
    }

    private int getResponseCode(HttpURLConnection conn) throws IOException {
        int responseCode = conn.getResponseCode();
        System.out.println("responseCode: " + responseCode);
        return responseCode;
    }

    private HttpURLConnection handleRedirect(String uaUrl, String urlDest, HttpURLConnection conn)
            throws IOException, MalformedURLException {
        System.out.println("handling redirect");
        String redirectResult = conn.getHeaderField(Constants.headerLocation);
        redirectResult = redirectResult.substring(0, redirectResult.indexOf('?'));
        this.getResponseAsString(conn);

        String cookieHeaderValue = conn.getHeaderField("Set-Cookie");
        String cookieVal = "";
        if (cookieHeaderValue != null) {
            int cookieEndIdx = cookieHeaderValue.indexOf(";");
            if (cookieEndIdx != -1) {
                cookieVal = cookieHeaderValue.substring(0, cookieEndIdx);
            }
        }

        System.out.println("redirect: " + redirectResult);

        URL url = new URL(redirectResult + "?" + uaUrl);
        HttpURLConnection newConn = (HttpURLConnection) url.openConnection();

        newConn.setRequestProperty(SimpleBusServiceImpl.USER_AGENT, SimpleBusServiceImpl.UA_IPHONE);
        newConn.setRequestProperty("Cookie", cookieVal);
        newConn.setInstanceFollowRedirects(false);
        newConn.getResponseCode();

        url = new URL(urlDest);
        newConn = (HttpURLConnection) url.openConnection();
        newConn.setRequestProperty(SimpleBusServiceImpl.USER_AGENT, SimpleBusServiceImpl.UA_IPHONE);
        newConn.setRequestProperty("Cookie", cookieVal);
        newConn.setInstanceFollowRedirects(false);

        return newConn;
    }

    private String getResponseAsString(HttpURLConnection conn) throws IOException {
        return this.getLinesAsString(conn);
    }

    private String getLinesAsString(HttpURLConnection conn) throws IOException {
        final InputStreamReader inputStreamReader = new InputStreamReader(conn.getInputStream(),
                Charset.forName(SimpleBusServiceImpl.UTF_8));
        final BufferedReader br = new BufferedReader(inputStreamReader, Constants.bufferDefaultSize);
        String line = br.readLine();
        StringBuilder sb = new StringBuilder();
        sb.append(line).append('\n');
        while (line != null) {
            line = br.readLine();
            sb.append(line).append('\n');
        }
        br.close();
        return sb.toString();
    }
}
